﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Processing;
using Cloo;
using Processing.Structures;

namespace RayTracer.Kernels
{
    /// <summary>
    /// Strahlenaufspaltung: Hierbei gilt es je nach Material und Einfallsrichtung des
    /// Ausgangsstrahls auf ein spezifisches Objekt, die Position und Richtung der beiden
    /// resultierenden Reflektions- und Refraktionsstahlen zu ermitteln.
    /// </summary>
    partial class KernelRaySplit : AKernel
    {
        #region Attribute
        #region GPU
        private Buffer<Float16> gpu_material;
        private Buffer<Float4> gpu_rayDir;
        private Buffer<Float4> gpu_normal;

        private Buffer<Float4> gpu_reflDir;
        private Buffer<Float4> gpu_refrDir;
        #endregion GPU
        #endregion Attribute

        #region Eigenschaften
        #region Dateninitialisierung
        /// <summary>
        /// Gibt und setzt die Materialien.
        /// </summary>
        public Buffer<Float16> Materials { get { return gpu_material; } set { gpu_material = value; } }

        /// <summary>
        /// Gibt und setzt die Strahlenrichtungen.
        /// </summary>
        public Buffer<Float4> RayDirections { get { return gpu_rayDir; } set { gpu_rayDir = value; } }

        /// <summary>
        /// Gibt und setzt die Normalen an den Schnittpunkten.
        /// </summary>
        public Buffer<Float4> Normals { get { return gpu_normal; } set { gpu_normal = value; } }
        
        /// <summary>
        /// Gibt die Reflektionsrichtung.
        /// </summary>
        public Buffer<Float4> ReflectionDirections
        {
            get
            {
                if (State != KernelState.Done) throw new Exception("Die Kernel wurde noch nicht berechnet.");
                return gpu_reflDir;
            }
        }

        /// <summary>
        /// Gibt die Refraktionsrichtungen
        /// </summary>
        public Buffer<Float4> RefractionDirections
        {
            get
            {
                if (State != KernelState.Done) throw new Exception("Die Kernel wurde noch nicht berechnet.");
                return gpu_refrDir;
            }
        }
        #endregion Dateninitialisierung

        #region Kernelausführung
        /// <summary>
        /// Gibt den Quellcode des Programms an.
        /// </summary>
        protected override string ProgramSource { get { return KERNEL_SOURCE; } }

        /// <summary>
        /// Gibt den Namen des Programms an.
        /// </summary>
        protected override string ProgramName { get { return KERNEL_MAIN; } }

        /// <summary>
        /// Gibt die Dimension des Programms an.
        /// </summary>
        protected override long[] Dimensions { get { return new long[] { gpu_rayDir.Length }; } }

        /// <summary>
        /// Gibt an ob die Kerneldaten valid initialisiert sind.
        /// </summary>
        protected override bool IsValidInitialized
        {
            get
            {
                if (gpu_material == null) throw new RayTracerException("Die Materialien müssen angegeben werden.");
                if (gpu_rayDir == null) throw new RayTracerException("Die Einfallsrichtungen müssen angegeben werden.");
                if (gpu_normal == null) throw new RayTracerException("Die Normalen müssen angegeben werden.");
                return
                    gpu_rayDir.Length == gpu_normal.Length &&
                    gpu_rayDir.Length == gpu_material.Length &&
                    gpu_rayDir.Length > 0;
            }
        }
        #endregion Kernelausführung

        /// <summary>
        /// Gibt eine hochgegriffene Schätzung über die Anzahl der Bytes pro Pixel/Ray.
        /// </summary>
        public static int BytesPerPixel { get { return (4 * (sizeof(float) * 4)) +  (sizeof(float) * 8); } }
        #endregion Eigenschaften

        #region Methoden
        /// <summary>
        /// Instanziiert und initialisiert die Kernel.
        /// </summary>
        /// <param name="processor">Der Prozessor.</param>
        public KernelRaySplit(Processor processor) : base(processor) { }

        /// <summary>
        /// Führt Aktionen vor der Ausführung der Kernel - mit dem Run-Aufruf aus. (z. B. Datentransport von CPU zu GPU)
        /// </summary>
        protected override void PrepareKernelExecution()
        {
            base.PrepareKernelExecution();
            gpu_reflDir = Processor.AllocateBuffer<Float4>(gpu_rayDir.Length);
            gpu_refrDir = Processor.AllocateBuffer<Float4>(gpu_rayDir.Length);
        }

        /// <summary>
        /// Setzt die Daten in Kernelargumente fest.
        /// </summary>
        protected override void SetArguments()
        {
            SetArgument(0, gpu_material);
            SetArgument(1, gpu_rayDir);
            SetArgument(2, gpu_normal);

            SetArgument(3, gpu_reflDir, true);
            SetArgument(4, gpu_refrDir);
        }
        #endregion Methoden
    }
}
